/**************************************************************************** 
* 
* Copyright (c) 2023  C*Core -   All Rights Reserved  
* 
* THIS SOFTWARE IS DISTRIBUTED "AS IS," AND ALL WARRANTIES ARE DISCLAIMED, 
* INCLUDING MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
* 
* PROJECT     : CCFC2011BC           
* DESCRIPTION : CCFC2011BC flash low level drivers code 
* HISTORY     : Initial version
* @file     flash_lld.c
* @version  1.1
* @date     2023 - 02 - 20
* @brief    Initial version.
*
*****************************************************************************/
#include "flash_lld.h"
#include "intc_lld.h"

/******************************************************************************************
 * Matters needing attention:
 * 1.Erase:   Blocks must be unlocked and selected first.
 * 2.Program: Blocks must be unlocked first.
 * 3.Read:    Pay attention to the troubleshooting of ECC problems.
 *******************************************************************************
 * CFlash: Bank0
 * *****************************************************************************
 * Sector * Module *   Sector address range    * Size * Address space lockingReg
 * (Block)* (Array)*                           * (KB) * CFLASH_LML * CFLASH_HBL
 * *****************************************************************************
 *   0    *   0    * 0x00000000u - 0x00007FFFu *  32  *    LLK0    *    ----
 *   1    *   0    * 0x00008000u - 0x0000BFFFu *  16  *    LLK1    *    ----
 *   2    *   0    * 0x0000C000u - 0x0000FFFFu *  16  *    LLK2    *    ----
 *   3    *   0    * 0x00010000u - 0x00017FFFu *  32  *    LLK3    *    ----
 *   4    *   0    * 0x00018000u - 0x0001FFFFu *  32  *    LLK4    *    ----
 *   5    *   0    * 0x00020000u - 0x0003FFFFu * 128  *    LLK5    *    ----
 *   6    *   0    * 0x00040000u - 0x0005FFFFu * 128  *    MLK0    *    ----
 *   7    *   0    * 0x00060000u - 0x0007FFFFu * 128  *    MLK1    *    ----
 * Shadow *   0    * 0x00200000u - 0x00203FFFu *  16  *    TSLK    *    ----
 *  Test  *   0    * 0x00400000u - 0x00403FFFu *  16  *    TSLK    *    ----
 *******************************************************************************
 *
 *******************************************************************************
 * DFlash: Bank1
 * *****************************************************************************
 * Sector * Module *   Sector address range    * Size * Address space lockingReg
 * (Block)* (Array)*                           * (KB) *       DFLASH_LML
 * *****************************************************************************
 *   0    *   0    * 0x00800000u - 0x00803FFFu *  16  *          LLK0
 *   1    *   0    * 0x00804000u - 0x00807FFFu *  16  *          LLK1
 *   2    *   0    * 0x00808000u - 0x0080BFFFu *  16  *          LLK2
 *   3    *   0    * 0x0080C000u - 0x0080FFFFu *  16  *          LLK3
 *(extend)*   0    * 0x00810000u - 0x0081FFFFu *  64  *          ----(unlocked)
 *  Test  *   0    * 0x00C00000u - 0x00C03FFFu *  16  *          TSLK
 *******************************************************************************/

typedef void (*Fls_AcCode_Type)(void);
static Fls_AcCode_Type Fls_AcCodePtr;
static uint32_t Fls_AcLoadArea[16] = {0}; /* PRQA S 3678 */

/*******************************************************************************
 * @brief      Determine the Flash address bank range (except Shadow/Test).
 *              CCFC2011BC40L5 -> SRAM/CFlash/DFlash: 64KB/512KB/128KB 
 * @param[in]  None
 * @param[out] None
 * @retval     uint32_t
 * @notapi
*******************************************************************************/
uint32_t PFlash_BankType(uint32_t addr)
{
    if(addr < (CFLASH_SECTOR7_START + CFLASH_SECTOR7_SIZE))
    {
        return CFLASH_BANK0;
    }
    if((addr >= DFLASH_SECTOR0_START) && \
            (addr < (DFLASH_EXTENSION_START + DFLASH_EXTENSION_SIZE)))
    {
        return DFLASH_BANK1;
    }
    if((addr >= 0x40000000u) && (addr < (0x40000000u + 0x20000u)))
    {
        return PSRAM_ADDR;
    }

    return ERROR_BANK;
}

/*******************************************************************************
 * @brief      CFlash Access Code.
 * @param[in]  None
 * @param[out] None
 * @retval     None
 * @notapi
*******************************************************************************/
static void Fls_AccessCode(void)
{
    /* Set MCR EHV(Enable High Voltage) bit, start operation */
    CFLASH.MCR.R |= CFLASH_MCR_EHV;

    /* Loop to wait for DONE = 1 */
    while ((CFLASH.MCR.R & CFLASH_MCR_DONE) != CFLASH_MCR_DONE)
    {
        ;
    }
}

/*******************************************************************************
 * @brief      CFlash Access Code Init.
 * @param[in]  None
 * @param[out] None
 * @retval     None
 * @notapi
*******************************************************************************/
void CFlash_AC_Init(void)
{
#ifdef CONFIG_FLASH
	/* Code in FLASH, Need load AC_CODE to SRAM */
	uint32_t * RomPtr = NULL;
	uint32_t * RomEndPtr = NULL;
	uint32_t * RamPtr = NULL;

	RomPtr = (uint32_t *)&Fls_AccessCode;
	RomEndPtr = RomPtr + (sizeof(Fls_AcLoadArea)/4);
	RamPtr = (uint32_t *)Fls_AcLoadArea;

    for(; (uint32_t)RomPtr < (uint32_t)RomEndPtr; RomPtr++)
    {
        *RamPtr = *RomPtr;
        RamPtr++;
    }

    Fls_AcCodePtr = (Fls_AcCode_Type)&Fls_AcLoadArea;
#else
	/* Code in SRAM, Do not need load AC_CODE to SRAM */
	Fls_AcCodePtr = (Fls_AcCode_Type)&Fls_AccessCode;
#endif
}

/***********************************************************************************
 * CFlash functional definition
 * @brief      CFlash Unlock Low/Mid Blocks (except Shadow/Test).
 *              [Password + Unlock]
 * @param[in]  None
 * @param[out] None
 * @retval     None
 * @notapi
*******************************************************************************/
static void CFlash_UnlockLowMidBlocksInit(void)
{
    /* Enter password */
    CFLASH.LML.R = FLASH_LML_KEY;
    CFLASH.SLL.R = FLASH_SLL_KEY;

    /* Unlock Blocks */
    CFLASH.LML.R = ((~CFLASH_LML_LOCK) & 0xFFFFFFFFU);
    CFLASH.SLL.R = ((~CFLASH_SLL_LOCK) & 0xFFFFFFFFU);
}

/*******************************************************************************
 * @brief      CFlash Lock Low/Mid Blocks (include Shadow/Test).
 *              [Password + Lock]
 * @param[in]  None
 * @param[out] None
 * @retval     None
 * @notapi
*******************************************************************************/
static void CFlash_LockLowMidBlocksInit(void)
{
    /* The default is locked (except Shadow/Test) */
    /* Enter password */
    CFLASH.LML.R = FLASH_LML_KEY;
    CFLASH.SLL.R = FLASH_SLL_KEY;

    /* Lock Blocks */
    CFLASH.LML.R = 0xFFFFFFFFU;
    CFLASH.SLL.R = 0xFFFFFFFFU;
}

/*******************************************************************************
 * @brief      CFlash Unlock High Blocks.
 *              [Password + Unlock]
 * @param[in]  None
 * @param[out] None
 * @retval     None
 * @notapi
*******************************************************************************/
static void CFlash_UnlockHighBlocksInit(void)
{
    /* Enter password */
    CFLASH.HBL.R = FLASH_HBL_KEY;

    /* Unlock Blocks */
    CFLASH.HBL.R = ((~CFLASH_HBL_LOCK) & 0xFFFFFFFFU);
}

/*******************************************************************************
 * @brief      CFlash Lock High Blocks.
 *              [Password + Lock]
 * @param[in]  None
 * @param[out] None
 * @retval     None
 * @notapi
*******************************************************************************/
static void CFlash_LockHighBlocksInit(void)
{
    /* The default is locked */
    /* Enter password */
    CFLASH.HBL.R = FLASH_HBL_KEY;

    /* Lock Blocks */
    CFLASH.HBL.R = 0xFFFFFFFFU;
}

/*******************************************************************************
 * @brief      CFlash Unlock All Blocks (except Shadow/Test).
 *              [Password + Unlock]
 * @param[in]  None
 * @param[out] None
 * @retval     None
 * @notapi
*******************************************************************************/
void CFlash_UnlockBlocksInit(void)
{
    CFlash_UnlockLowMidBlocksInit();
    CFlash_UnlockHighBlocksInit();
}

/*******************************************************************************
 * @brief      CFlash Lock All Blocks (include Shadow/Test).
 *              [Password + Lock]
 * @param[in]  None
 * @param[out] None
 * @retval     None
 * @notapi
*******************************************************************************/
void CFlash_LockBlocksInit(void)
{
    CFlash_LockLowMidBlocksInit();
    CFlash_LockHighBlocksInit();
}

/*******************************************************************************
 * @brief      CFlash Select Low/Mid/High Blocks
 *              (Shadow/Test does not have the selection function).
 * @param[in]  lsl/msl: uint32_t
 *             hsl: uint32_t
 * @param[out] None
 * @retval     None
 * @notapi
*******************************************************************************/
void CFlash_SelectBlocks(uint32_t lsl, uint32_t msl, uint32_t hsl)
{
    /* Select Low/Mid Address Space Block */
    CFLASH.LMS.R = (lsl | msl);

    /* Select High Address Space Block */
    CFLASH.HBS.R = hsl;
}

/*******************************************************************************
 * @brief      CFlash UnSelect All Blocks
 *              (Shadow/Test does not have the selection function).
 * @param[in]  None
 * @param[out] None 
 * @retval     None
 * @notapi
*******************************************************************************/
void CFlash_UnSelectBlocks(void)
{
    CFLASH.LMS.R = CFLASH_LMS_NULL;
    CFLASH.HBS.R = CFLASH_HBS_NULL;
}

/*******************************************************************************
 * @brief      Returns the selected block by CFlash address 
 *                  (except Shadow/Test).
 * @param[in]  addr
 * @param[out] lsl/msl: uint32_t
 *             hsl: uint32_t
 * @retval     0:success 1:fail(Shadow/Test or other addr)
 * @notapi
*******************************************************************************/
uint32_t CFlash_GetBlockSelect(uint32_t addr, uint32_t *lsl, uint32_t *msl, uint32_t *hsl)
{
    *lsl = CFLASH_LMS_NULL;
    *msl = CFLASH_LMS_NULL;
    *hsl = CFLASH_HBS_NULL;

    /* CFlash Block Select(except Shadow/Test) */
    if (addr < CFLASH_SECTOR1_START)
    {
        *lsl = CFLASH_LMS_LSL0;
        return 0U;
    }
    if (addr < CFLASH_SECTOR2_START)
    {
        *lsl = CFLASH_LMS_LSL1;
        return 0U;
    }
    if (addr < CFLASH_SECTOR3_START)
    {
        *lsl = CFLASH_LMS_LSL2;
        return 0U;
    }
    if (addr < CFLASH_SECTOR4_START)
    {
        *lsl = CFLASH_LMS_LSL3;
        return 0U;
    }
    if (addr < CFLASH_SECTOR5_START)
    {
        *lsl = CFLASH_LMS_LSL4;
        return 0U;
    }
    if (addr < CFLASH_SECTOR6_START)
    {
        *lsl = CFLASH_LMS_LSL5;
        return 0U;
    }
    if (addr < CFLASH_SECTOR7_START)
    {
        *msl = CFLASH_LMS_MSL0;
        return 0U;
    }
    if (addr < (CFLASH_SECTOR7_START + CFLASH_SECTOR7_SIZE))
    {
        *msl = CFLASH_LMS_MSL1;
        return 0U;
    }

    /* addr fail, Shadow/Test does not have the selection function */
    return 1U;
}

/*******************************************************************************
 * @brief      Get CFlash Low Sector StartAddress and Size by the selected block 
 *                  (except Shadow/Test).
 * @param[in]  lsl:  uint32_t
 * @param[out] addr: CFlash Low Sector StartAddress
 *             size: CFlash Low Sector Size
 * @retval     0:success 2:fail(lsl is not exist)
 * @notapi
*******************************************************************************/
uint32_t CFlash_GetLowSectorAddrSize(uint32_t lsl, uint32_t *addr, uint32_t *size)
{
    uint32_t ret = 0x00000000U;

    switch (lsl)
    {
        case CFLASH_LMS_LSL0:
            *addr = CFLASH_SECTOR0_START;
            *size = CFLASH_SECTOR0_SIZE;
            break;
        case CFLASH_LMS_LSL1:
            *addr = CFLASH_SECTOR1_START;
            *size = CFLASH_SECTOR1_SIZE;
            break;
        case CFLASH_LMS_LSL2:
            *addr = CFLASH_SECTOR2_START;
            *size = CFLASH_SECTOR2_SIZE;
            break;
        case CFLASH_LMS_LSL3:
            *addr = CFLASH_SECTOR3_START;
            *size = CFLASH_SECTOR3_SIZE;
            break;
        case CFLASH_LMS_LSL4:
            *addr = CFLASH_SECTOR4_START;
            *size = CFLASH_SECTOR4_SIZE;
            break;
        case CFLASH_LMS_LSL5:
            *addr = CFLASH_SECTOR5_START;
            *size = CFLASH_SECTOR5_SIZE;
            break;
        case CFLASH_LMS_L0_5:   /* Select: LSL0~5 */
        case CFLASH_L0_5_M0_1:  /* Select: LSL0~5 */
            *addr = CFLASH_SECTOR0_START;
            *size = CFLASH_LOW0_5_SIZE;
            break;
        case CFLASH_LMS_NULL:
        default:
            ret = 0x00000002U;
            break;
    }

    return ret;
}

/*******************************************************************************
 * @brief      Get CFlash Mid Sector StartAddress and Size by the selected block 
 *                  (except Shadow/Test).
 * @param[in]  msl:  uint32_t
 * @param[out] addr: CFlash Mid Sector StartAddress
 *             size: CFlash Mid Sector Size
 * @retval     0:success 2:fail(msl is not exist)
 * @notapi
*******************************************************************************/
uint32_t CFlash_GetMidSectorAddrSize(uint32_t msl, uint32_t *addr, uint32_t *size)
{
    uint32_t ret = 0x00000000U;

    switch (msl)
    {
        case CFLASH_LMS_MSL0:
            *addr = CFLASH_SECTOR6_START;
            *size = CFLASH_SECTOR6_SIZE;
            break;
        case CFLASH_LMS_MSL1:
            *addr = CFLASH_SECTOR7_START;
            *size = CFLASH_SECTOR7_SIZE;
            break;
        case CFLASH_LMS_M0_1:   /* Select: MSL0~1 */
        case CFLASH_L0_5_M0_1:  /* Select: MSL0~1 */
            *addr = CFLASH_SECTOR6_START;
            *size = CFLASH_MID0_1_SIZE;
            break;
        case CFLASH_LMS_NULL:
        default:
            ret = 0x00000002U;
            break;
    }

    return ret;
}

/*******************************************************************************
 * @brief      CFlash Unlocks the selected block (except Shadow/Test).
 * @param[in]  lsl/msl: uint32_t
 *             hsl: uint32_t
 * @param[out] None
 * @retval     None
 * @notapi
*******************************************************************************/
void CFlash_UnlockSelectBlock(uint32_t lsl, uint32_t msl, uint32_t hsl)
{
    /* Enter password */
    CFLASH.LML.R = FLASH_LML_KEY;
    CFLASH.SLL.R = FLASH_SLL_KEY;
    CFLASH.HBL.R = FLASH_HBL_KEY;

    /* Unlock Blocks */
    CFLASH.LML.R = (~(lsl|msl));
    CFLASH.SLL.R = (~(lsl|msl));
    CFLASH.HBL.R = (~hsl);
}

/*******************************************************************************
 * @brief      CFlash Block(Sector) Erase (except Shadow/Test).
 *              1. If you want to erase the block, you must [unlock]
 *                  and [select] the block.
 *              2. CFlash cannot erase/programming CFlash, 
 *                 Functions needs to run in RAM.
 * @param[in]  lsl/msl: uint32_t
 *             hsl: uint32_t
 * @param[out] None
 * @retval     0: success, other: fail
 * @notapi
*******************************************************************************/
uint32_t CFlash_BlockErase(uint32_t lsl, uint32_t msl, uint32_t hsl)
{
    uint32_t ret = 0x00000000U;
    uint32_t lsl_sel, msl_sel, hsl_sel;

    lsl_sel = lsl;
    msl_sel = msl;
    hsl_sel = hsl;

    if ((CFLASH.MCR.R & (CFLASH_MCR_ERS | CFLASH_MCR_PGM)) != 0U)
    {
        /* If any P/E operation in progress return error */
        ret = 0x00000001U;
    }
    else
    {
        /* Set MCR ERS(Erase) bit */
        CFLASH.MCR.R = CFLASH_MCR_ERS;

        /* Prepare low and middle to select blocks */
        lsl_sel &= CFLASH_LMS_L0_5;
        msl_sel &= CFLASH_LMS_M0_1;
        hsl_sel &= CFLASH_HBS_NULL;

        /* Write the block selection registers */
        CFlash_SelectBlocks(lsl_sel, msl_sel, hsl_sel);

        /* Interlock write */
        *((volatile uint32_t *)(CFLASH_SECTOR0_START)) = 0xFFFFFFFFu; /* PRQA S 2810 */

        /* Set MCR EHV(Enable High Voltage) bit, start operation */
        Fls_AcCodePtr();

        /* Check PEG(Program/Erase Good) flag */
        if ((CFLASH.MCR.R & CFLASH_MCR_PEG) != CFLASH_MCR_PEG)
        {
            ret = 0x000000FFU;
        }

        /* Reset EHV */
        CFLASH.MCR.R = CFLASH_MCR_ERS;

        /* Reset ERS */
        CFLASH.MCR.R = CFLASH_MCR_NULL;

        /* Loop to wait for ERS = 0 */
        while ((CFLASH.MCR.R & CFLASH_MCR_ERS) == CFLASH_MCR_ERS)
        {
            ;
        }

        /* Write the block selection registers, UnSelect blocks */
        CFlash_UnSelectBlocks();
    }

    return ret;
}

/*******************************************************************************
 * @brief      CFlash Chip Erase (except Shadow/Test).
 *              1. Must be unlocked first.
 *              2. CFlash cannot erase/programming CFlash, 
 *                 Functions needs to run in RAM.
 * @param[in]  None
 * @param[out] None
 * @retval     0:success other:fail
 * @notapi
*******************************************************************************/
uint32_t CFlash_ChipErase(void)
{
    uint32_t retStatus = 0x00000000U;

    retStatus = CFlash_BlockErase(CFLASH_LMS_L0_5, CFLASH_LMS_M0_1, CFLASH_HBS_NULL);

    return retStatus;
}

/*******************************************************************************
 * @brief      CFlash Program.
 *              1. If you want to program the block, you must [unlock]
 *                  the block.
 *              2. CFlash cannot erase/programming CFlash, 
 *                 Functions needs to run in RAM.
 * @param[in]  addr: destination address
 *             size: program size
 *             data: source data
 * @param[out] None
 * @retval     0: success, other: fail
 * @notapi
*******************************************************************************/
uint32_t CFlash_Program(uint32_t addr, uint32_t size, const uint32_t *data)
{
    uint32_t ret = 0x00000000U;
    uint32_t counter;                       /* Loop variable counter */
    uint32_t temp;                          /* Temporary variable */
    uint32_t addr_tmp;
    const uint32_t *data_tmp;

    addr_tmp = addr;
    data_tmp = data;

    if (size > 0U)
    {
        /* Check operation */
        if ((CFLASH.MCR.R & (CFLASH_MCR_ERS | CFLASH_MCR_PGM)) != 0U)
        {
            /* Reuse variable counter for checking erase suspend */
            counter = CFLASH.MCR.R & (CFLASH_MCR_ERS | CFLASH_MCR_ESUS);
            if (counter == (CFLASH_MCR_ERS | CFLASH_MCR_ESUS))
            {
                /* Erase has been suspended
                 *      and program operation can be performed. */
            }
            else
            {
                /* If any P/E operation in progress return error */
                ret = 0x00000001U;
            }
        }

        /* Program sequence */
        if (ret == 0x00000000U)
        {
            /* Number of double words need to be programmed */
            if ((size % (PGM_SIZE)) != 0U)
            {
                temp = (size / (PGM_SIZE)) + 1U;
            }
            else
            {
                temp = (size / (PGM_SIZE));
            }

            /* Set MCR PGM(Program) bit */
            CFLASH.MCR.R = CFLASH_MCR_PGM;

            for (counter = 0U; counter < temp; counter++)
            {
                /* Write interlock */
                *(vuint32_t *)addr_tmp = *data_tmp;
                addr_tmp += 4U;
                data_tmp++;
                *(vuint32_t *)addr_tmp = *data_tmp;
                addr_tmp += 4U;
                data_tmp++;

                /* Set MCR EHV(Enable High Voltage) bit, start operation */
                Fls_AcCodePtr();

                /* Check PEG(Program/Erase Good) flag */
                if ((CFLASH.MCR.R & CFLASH_MCR_PEG) != CFLASH_MCR_PEG)
                {
                    /* Reset EHV */
                    CFLASH.MCR.R = CFLASH_MCR_PGM;
                    ret = 0x000000FFU;
                    break;
                }

                /* Reset EHV */
                CFLASH.MCR.R = CFLASH_MCR_PGM;
            }
            /* Reset PGM */
            CFLASH.MCR.R = CFLASH_MCR_NULL;

            /* Loop to wait for PGM = 0 */
            while ((CFLASH.MCR.R & CFLASH_MCR_PGM) == CFLASH_MCR_PGM)
            {
                ;
            }
        }
    }

    return ret;
}

/*******************************************************************************
 * @brief      CFlash Word Read.
 * @param[in]  addr: destination address
 * @param[out] None
 * @retval     Flash data
 * @notapi
 *******************************************************************************/
uint32_t CFlash_WordRead(uint32_t addr)
{
    uint32_t ret;

    /* Setup ECC detection method */
#if (EER_OPTION == IVOR_EXCEPTION)
    uint32_t temp_MSR;      /* e200z_ Machine State Register (MSR) */
    uint32_t temp_INTC_CPR; /* INTC Current Priority REG (INTC_CPR) */

    /* Save old MSR and disable MSR_EE */
    MFMSR(temp_MSR); /* PRQA S 1006 */
    DIS_MSR_EE(); /* PRQA S 1006 */

    /* If MSR_EE is off before reading Flash, change the current interrupt 
     * priority to prevent unnecessary external interrupts */
    if ((temp_MSR & MSR_EE) == 0U)
    {
        /* Save old INTC_CPR */
        temp_INTC_CPR = INTC_GET_CPR();
        /* Set the current priority to the highest */
        INTC_CPR_SET((uint32_t)CPR_PRI_15);
    }

    /* Enable MSR_EE to prevent ECC errors during read */
    MTMSR(MSR_EE); /* PRQA S 1006 */
#endif

    /* Read data */
    ret = *((vuint32_t *)addr);

#if (EER_OPTION == IVOR_EXCEPTION)
    /* Restore MSR */
    MTMSR(temp_MSR); /* PRQA S 1006 */

    /* If MSR_EE is off before reading Flash, Restore INTC_CPR */
    if ((temp_MSR & MSR_EE) == 0U)
    {
        /* Restore INTC_CPR */
        INTC_CPR_SET(temp_INTC_CPR);
    }
#endif

    return ret;
}

/*******************************************************************************
 * @brief      CFlash Byte Read.
 * @param[in]  addr: destination address
 * @param[out] None
 * @retval     Flash data
 * @notapi
 *******************************************************************************/
uint8_t CFlash_ByteRead(uint32_t addr)
{
    uint8_t ret;

    /* Setup ECC detection method */
#if (EER_OPTION == IVOR_EXCEPTION)
    uint32_t temp_MSR;      /* e200z_ Machine State Register (MSR) */
    uint32_t temp_INTC_CPR; /* INTC Current Priority REG (INTC_CPR) */

    /* Save old MSR and disable MSR_EE */
    MFMSR(temp_MSR); /* PRQA S 1006 */
    DIS_MSR_EE(); /* PRQA S 1006 */

    /* If MSR_EE is off before reading Flash, change the current interrupt 
     * priority to prevent unnecessary external interrupts */
    if ((temp_MSR & MSR_EE) == 0U)
    {
        /* Save old INTC_CPR */
        temp_INTC_CPR = INTC_GET_CPR();
        /* Set the current priority to the highest */
        INTC_CPR_SET((uint32_t)CPR_PRI_15);
    }

    /* Enable MSR_EE to prevent ECC errors during read */
    MTMSR(MSR_EE); /* PRQA S 1006 */
#endif

    /* Read data */
    ret = *((uint8_t *)addr);

#if (EER_OPTION == IVOR_EXCEPTION)
    /* Restore MSR */
    MTMSR(temp_MSR); /* PRQA S 1006 */

    /* If MSR_EE is off before reading Flash, Restore INTC_CPR */
    if ((temp_MSR & MSR_EE) == 0U)
    {
        /* Restore INTC_CPR */
        INTC_CPR_SET(temp_INTC_CPR);
    }
#endif

    return ret;
}

/************************************************************************ 
 *    DFlash functional definition
 ************************************************************************/
/*******************************************************************************
 * @brief      DFlash Unlock Low/Mid Blocks (except Test).
 *             [Password + Unlock]
 * @param[in]  None
 * @param[out] None
 * @retval     None
 * @notapi
 *******************************************************************************/
static void DFlash_UnlockLowMidBlocksInit(void)
{
    /* Enter password */
    DFLASH.LML.R = FLASH_LML_KEY;
    DFLASH.SLL.R = FLASH_SLL_KEY;

    /* Unlock Blocks */
    DFLASH.LML.R = (~DFLASH_LML_LOCK);
    DFLASH.SLL.R = (~DFLASH_SLL_LOCK);
}

/*******************************************************************************
 * @brief      DFlash Lock Low/Mid Blocks (include Test).
 *              [Password + Lock]
 * @param[in]  None
 * @param[out] None
 * @retval     None
 * @notapi
 *******************************************************************************/
static void DFlash_LockLowMidBlocksInit(void)
{
    /* The default is locked (except Test) */
    /* Enter password */
    DFLASH.LML.R = FLASH_LML_KEY;
    DFLASH.SLL.R = FLASH_SLL_KEY;

    /* Lock Blocks */
    DFLASH.LML.R = 0xFFFFFFFFU;
    DFLASH.SLL.R = 0xFFFFFFFFU;
}

/*******************************************************************************
 * @brief      DFlash Unlock All Blocks (except Test).
 *              [Password + Unlock]
 * @param[in]  None
 * @param[out] None
 * @retval     None
 * @notapi
 *******************************************************************************/
void DFlash_UnlockBlocksInit(void)
{
    DFlash_UnlockLowMidBlocksInit();
}

/*******************************************************************************
 * @brief      DFlash Lock All Blocks (include Test).
 *              [Password + Lock]
 * @param[in]  None
 * @param[out] None
 * @retval     None
 * @notapi
 *******************************************************************************/
void DFlash_LockBlocksInit(void)
{
    DFlash_LockLowMidBlocksInit();
}

/*******************************************************************************
 * @brief      DFlash Select Low Blocks
 *              (Test does not have the selection function).
 * @param[in]  lsl: uint32_t
 * @param[out] None
 * @retval     None
 * @notapi
 *******************************************************************************/
void DFlash_SelectBlocks(uint32_t lsl)
{
    /* Select Low Address Space Block */
    if ((lsl != DFLASH_LMS_NULL) && (lsl <= DFLASH_LMS_L0_3))
    {
        DFLASH.LMS.R = lsl;
    }
}

/*******************************************************************************
 * @brief      DFlash UnSelect All Blocks
 *              (Test does not have the selection function).
 * @param[in]  None
 * @param[out] None 
 * @retval     None
 * @notapi
 *******************************************************************************/
void DFlash_UnSelectBlocks(void)
{
    DFLASH.LMS.R = DFLASH_LMS_NULL;
}

/*******************************************************************************
 * @brief      Returns the selected block by DFlash address 
 *                  (except Extension / Test).
 * @param[in]  addr
 * @param[out] lsl: uint32_t
 * @retval     0:success 1:fail(Test or other addr)
 * @notapi
  *******************************************************************************/
uint32_t DFlash_GetBlockSelect(uint32_t addr, uint32_t *lsl)
{
    *lsl = DFLASH_LMS_NULL;

    /* DFlash Block Select(except Test) */
    if (addr < DFLASH_SECTOR0_START)
    {
        /* addr fail */
        return 1U;
    }
    if (addr < DFLASH_SECTOR1_START)
    {
        *lsl = DFLASH_LMS_LSL0;
        return 0U;
    }
    if (addr < DFLASH_SECTOR2_START)
    {
        *lsl = DFLASH_LMS_LSL1;
        return 0U;
    }
    if (addr < DFLASH_SECTOR3_START)
    {
        *lsl = DFLASH_LMS_LSL2;
        return 0U;
    }
    if (addr < (DFLASH_SECTOR3_START + DFLASH_SECTOR3_SIZE))
    {
        *lsl = DFLASH_LMS_LSL3;
        return 0U;
    }

    /* addr fail, Test does not have the selection function */
    return 1U;
}

/*******************************************************************************
 * @brief      Get DFlash Low Sector StartAddress and Size by the selected block 
 *                  (except Test).
 * @param[in]  lsl:  uint32_t
 * @param[out] addr: DFlash Low Sector StartAddress
 *             size: DFlash Low Sector Size
 * @retval     0:success 1:fail(lsl is not exist)
 * @notapi
  *******************************************************************************/
uint32_t DFlash_GetLowSectorAddrSize(uint32_t lsl, uint32_t *addr, uint32_t *size)
{
    uint32_t ret = 0x00000000U;

    switch (lsl)
    {
        case DFLASH_LMS_LSL0:
            *addr = DFLASH_SECTOR0_START;
            *size = DFLASH_SECTOR0_SIZE;
            break;
        case DFLASH_LMS_LSL1:
            *addr = DFLASH_SECTOR1_START;
            *size = DFLASH_SECTOR1_SIZE;
            break;
        case DFLASH_LMS_LSL2:
            *addr = DFLASH_SECTOR2_START;
            *size = DFLASH_SECTOR2_SIZE;
            break;
        case DFLASH_LMS_LSL3:
            *addr = DFLASH_SECTOR3_START;
            *size = DFLASH_SECTOR3_SIZE;
            break;
        case DFLASH_LMS_L0_3:   /* Select: LSL0~3 */
            *addr = DFLASH_SECTOR0_START;
            *size = DFLASH_LOW0_3_SIZE;
            break;
        case DFLASH_LMS_NULL:
        default:
            ret = 0x00000001U;
            break;
    }

    return ret;
}

/*******************************************************************************
 * @brief      DFlash Unlocks the selected block (except Extension / Test).
 * @param[in]  lsl: uint32_t
 * @param[out] None
 * @retval     
 * @notapi
  *******************************************************************************/
void DFlash_UnlockSelectBlock(uint32_t lsl)
{
    /* Unlock Low Address Space Block */
    if ((lsl != DFLASH_LMS_NULL) && (lsl <= DFLASH_LMS_L0_3))
    {
        /* Enter password */
        DFLASH.LML.R = FLASH_LML_KEY;
        DFLASH.SLL.R = FLASH_SLL_KEY;

        /* Unlock Blocks */
        DFLASH.LML.R = (~lsl);
        DFLASH.SLL.R = (~lsl);
    }
}


/*******************************************************************************
 * @brief      DFlash Block(Sector) Erase (except Extension / Test).
 *              If you want to erase the block, you must [unlock]
 *                  and [select] the block.
 * @param[in]  lsl: uint32_t
 * @param[out] None
 * @retval     0: success, other: fail
 * @notapi
 *******************************************************************************/
uint32_t DFlash_BlockErase(uint32_t lsl)
{
    uint32_t ret = 0x00000000U;

    if ((DFLASH.MCR.R & (DFLASH_MCR_ERS | DFLASH_MCR_PGM)) != 0U)
    {
        /* If any P/E operation in progress return error */
        ret = 0x00000001U;
    }
    else
    {
        /* Set MCR ERS(Erase) bit */
        DFLASH.MCR.R = DFLASH_MCR_ERS;

        /* Write the block selection registers */
        DFlash_SelectBlocks(lsl);

        /* Interlock write */
        *((volatile uint32_t *)(DFLASH_SECTOR0_START)) = 0xFFFFFFFFU;

        /* Set MCR EHV(Enable High Voltage) bit, start operation */
        DFLASH.MCR.R |= DFLASH_MCR_EHV;

        /* Loop to wait for DONE = 1 */
        while ((DFLASH.MCR.R & DFLASH_MCR_DONE) != DFLASH_MCR_DONE)
        {
            ;
        }

        /* Check PEG(Program/Erase Good) flag */
        if ((DFLASH.MCR.R & DFLASH_MCR_PEG) != DFLASH_MCR_PEG)
        {
            ret = 0x000000FFU;
        }

        /* Reset EHV */
        DFLASH.MCR.R = DFLASH_MCR_ERS;

        /* Reset ERS */
        DFLASH.MCR.R = DFLASH_MCR_NULL;

        /* Write the block selection registers, UnSelect blocks */
        DFlash_UnSelectBlocks();
    }

    return ret;
}

/*******************************************************************************
 * @brief      DFlash Chip Erase (except Test).
 *              Must be unlocked first.
 * @param[in]  None
 * @param[out] None
 * @retval     0:success other:fail
 * @notapi
 *******************************************************************************/
uint32_t DFlash_ChipErase(void)
{
    uint32_t retStatus = 0x00000000U;

    retStatus = DFlash_BlockErase(DFLASH_LMS_L0_3);

    return retStatus;
}
/*******************************************************************************
 * @brief      DFlash Program.
 *              If you want to program the block, you must [unlock]
 *                  the block.
 * @param[in]  addr: destination address
 *             size: program size
 *             data: source data
 * @param[out] None
 * @retval     0: success, other: fail
 * @notapi
 *******************************************************************************/
uint32_t DFlash_Program(uint32_t addr, uint32_t size, const uint32_t *data)
{
    uint32_t ret = 0x00000000U;
    uint32_t counter;                       /* Loop variable counter */
    uint32_t temp;                          /* Temporary variable */
    uint32_t addr_tmp;
    const uint32_t *data_tmp;

    addr_tmp = addr;
    data_tmp = data;

    if (size > 0U)
    {
        /* Check operation */
        if ((DFLASH.MCR.R & (DFLASH_MCR_ERS | DFLASH_MCR_PGM)) != 0U)
        {
            /* Reuse variable counter for checking erase suspend */
            counter = DFLASH.MCR.R & (DFLASH_MCR_ERS | DFLASH_MCR_ESUS);
            if (counter == (DFLASH_MCR_ERS | DFLASH_MCR_ESUS))
            {
                /* Erase has been suspended
                 *      and program operation can be performed. */
            }
            else
            {
                /* If any P/E operation in progress return error */
                ret = 0x00000001U;
            }
        }

        /* Program sequence */
        if (ret == 0x00000000U)
        {
            /* Number of double words need to be programmed */
            if ((size % PGM_SIZE) != 0U)
            {
                temp = (size / PGM_SIZE) + 1U;
            }
            else
            {
                temp = (size / PGM_SIZE);
            }

            /* Set MCR PGM(Program) bit */
            DFLASH.MCR.R = DFLASH_MCR_PGM;

            for (counter = 0U; counter < temp; counter++)
            {
                /* Write interlock */
                *(vuint32_t *)addr_tmp = *data_tmp;
                addr_tmp += 4U;
                data_tmp++;
                *(vuint32_t *)addr_tmp = *data_tmp;
                addr_tmp += 4U;
                data_tmp++;

                /* Set MCR EHV(Enable High Voltage) bit, start operation */
                DFLASH.MCR.R |= DFLASH_MCR_EHV;

                /* Loop to wait for DONE = 1 */
                while ((DFLASH.MCR.R & DFLASH_MCR_DONE) != DFLASH_MCR_DONE)
                {
                    ;
                }

                /* Check PEG(Program/Erase Good) flag */
                if ((DFLASH.MCR.R & DFLASH_MCR_PEG) != DFLASH_MCR_PEG)
                {
                    /* Reset EHV */
                    DFLASH.MCR.R = DFLASH_MCR_PGM;
                    ret = 0x000000FFU;
                    break;
                }

                /* Reset EHV */
                DFLASH.MCR.R = DFLASH_MCR_PGM;
            }
            /* Reset PGM */
            DFLASH.MCR.R = DFLASH_MCR_NULL;
        }
    }

    return ret;
}

/*******************************************************************************
 * @brief      DFlash Word Read.
 * @param[in]  addr: destination address
 * @param[out] None
 * @retval     Flash data
 * @notapi
 *******************************************************************************/
uint32_t DFlash_WordRead(uint32_t addr)
{
    uint32_t ret;

    /* Setup ECC detection method */
#if (EER_OPTION == IVOR_EXCEPTION)
    uint32_t temp_MSR;      /* e200z_ Machine State Register (MSR) */
    uint32_t temp_INTC_CPR; /* INTC Current Priority REG (INTC_CPR) */

    /* Save old MSR and disable MSR_EE */
    MFMSR(temp_MSR); /* PRQA S 1006 */
    DIS_MSR_EE(); /* PRQA S 1006 */

    /* If MSR_EE is off before reading Flash, change the current interrupt 
     * priority to prevent unnecessary external interrupts */
    if ((temp_MSR & MSR_EE) == 0U)
    {
        /* Save old INTC_CPR */
        temp_INTC_CPR = INTC_GET_CPR();
        /* Set the current priority to the highest */
        INTC_CPR_SET((uint32_t)CPR_PRI_15);
    }

    /* Enable MSR_EE to prevent ECC errors during read */
    MTMSR(MSR_EE); /* PRQA S 1006 */
#endif

    /* Read data */
    ret = *((vuint32_t *)addr);

#if (EER_OPTION == IVOR_EXCEPTION)
    /* Restore MSR */
    MTMSR(temp_MSR); /* PRQA S 1006 */

    /* If MSR_EE is off before reading Flash, Restore INTC_CPR */
    if ((temp_MSR & MSR_EE) == 0U)
    {
        /* Restore INTC_CPR */
        INTC_CPR_SET(temp_INTC_CPR);
    }
#endif

    return ret;
}

/*******************************************************************************
 * @brief      DFlash Byte Read.
 * @param[in]  addr: destination address
 * @param[out] None
 * @retval     Flash data
 * @notapi
 *******************************************************************************/
uint8_t DFlash_ByteRead(uint32_t addr)
{
    uint8_t ret;

    /* Setup ECC detection method */
#if (EER_OPTION == IVOR_EXCEPTION)
    uint32_t temp_MSR;      /* e200z_ Machine State Register (MSR) */
    uint32_t temp_INTC_CPR; /* INTC Current Priority REG (INTC_CPR) */

    /* Save old MSR and disable MSR_EE */
    MFMSR(temp_MSR); /* PRQA S 1006 */
    DIS_MSR_EE(); /* PRQA S 1006 */

    /* If MSR_EE is off before reading Flash, change the current interrupt 
     * priority to prevent unnecessary external interrupts */
    if ((temp_MSR & MSR_EE) == 0U)
    {
        /* Save old INTC_CPR */
        temp_INTC_CPR = INTC_GET_CPR();
        /* Set the current priority to the highest */
        INTC_CPR_SET((uint32_t)CPR_PRI_15);
    }

    /* Enable MSR_EE to prevent ECC errors during read */
    MTMSR(MSR_EE); /* PRQA S 1006 */
#endif

    /* Read data */
    ret = *((uint8_t *)addr);

#if (EER_OPTION == IVOR_EXCEPTION)
    /* Restore MSR */
    MTMSR(temp_MSR); /* PRQA S 1006 */

    /* If MSR_EE is off before reading Flash, Restore INTC_CPR */
    if ((temp_MSR & MSR_EE) == 0U)
    {
        /* Restore INTC_CPR */
        INTC_CPR_SET(temp_INTC_CPR);
    }
#endif

    return ret;
}

/*******************************************************************************
 * @brief      CFlash_BlockSelect_Cancel
 *             Cancel block/sector selection.
 *             Page(8K) Erase:
 *             If you want to erase the page, 
 *             you need to cancel the sector selection.
 * @param[in]  uint32_t addr
 * @param[out] None
 * @retval     None
 * @notapi
 *******************************************************************************/
void CFlash_BlockSelect_Cancel(void)
{
    /** CFlash Low/Mid Address Space Block Select Register(CFLASH_LMS) **/
    CFLASH.LMS.R = 0x00000000U;
    /** CFlash High Address Space Block Select Register (CFLASH_HBS)   **/
    CFLASH.HBS.R = 0x00000000U;
}

/*******************************************************************************
 * @brief      CFlash_PageErase
 * @param[in]  uint32_t addr
 * @param[out] None
 * @retval     None
 * @notapi
 *******************************************************************************/
uint32_t CFlash_PageErase(uint32_t addr)
{
    uint32_t ret = 0x00000000U;

    if ((CFLASH.MCR.R & (CFLASH_MCR_ERS | CFLASH_MCR_PGM)) != 0U)
    {
        /* If any P/E operation in progress return error */
        ret = 0x00000001U;
    }
    else
    {
        /* Set MCR ERS(Erase) bit */
        CFLASH.MCR.R = CFLASH_MCR_ERS;

        /* Interlock write */
        *( (volatile uint32_t *)(addr) ) = 0xFFFFFFFFU; /* PRQA S 2813 */

        /* Set MCR EHV(Enable High Voltage) bit, start operation */
        Fls_AcCodePtr();

        /* Check PEG(Program/Erase Good) flag */
        if ((CFLASH.MCR.R & CFLASH_MCR_PEG) != CFLASH_MCR_PEG)
        {
            ret = 0x000000FFU;
        }

        /* Reset EHV */
        CFLASH.MCR.R = CFLASH_MCR_ERS;

        /* Reset ERS */
        CFLASH.MCR.R = CFLASH_MCR_NULL;

        /* Loop to wait for ERS = 0 */
        while ((CFLASH.MCR.R & CFLASH_MCR_ERS) == CFLASH_MCR_ERS)
        {
            ;
        }
    }
    return ret;
}

/*******************************************************************************
 * @brief      CFlash Double Word Program.
 *              1. If you want to program the block, you must [unlock]
 *                  the block.
 *              2. CFlash cannot erase/programming CFlash, 
 *                 Functions needs to run in RAM.
 * @param[in]  word1: word data 1
 *             word2: word data 2
 *             addr: destination address
 * @param[out] None
 * @retval     0: success, other: fail
 * @notapi
 *******************************************************************************/
uint32_t CFlash_DwProgram(uint32_t word1, uint32_t word2, uint32_t addr)
{
    uint32_t ret = 0x00000000U;
    uint32_t counter;                       /* Loop variable counter */

    /* Check operation */
    if ((CFLASH.MCR.R & (CFLASH_MCR_ERS | CFLASH_MCR_PGM)) != 0U)
    {
        /* Reuse variable counter for checking erase suspend */
        counter = CFLASH.MCR.R & (CFLASH_MCR_ERS | CFLASH_MCR_ESUS);
        if (counter == (CFLASH_MCR_ERS | CFLASH_MCR_ESUS))
        {
            /* Erase has been suspended
             *      and program operation can be performed. */
        }
        else
        {
            /* If any P/E operation in progress return error */
            ret = 0x00000001U;
        }
    }

    /* Program sequence */
    if (ret == 0x00000000U)
    {
        /* Set MCR PGM(Program) bit */
        CFLASH.MCR.R = CFLASH_MCR_PGM;

        /* Write interlock */
        *( (vuint32_t *)(addr) ) = word1;
        *( (vuint32_t *)(addr + 4U) ) = word2;

        /* Set MCR EHV(Enable High Voltage) bit, start operation */
        Fls_AcCodePtr();

        /* Check PEG(Program/Erase Good) flag */
        if ((CFLASH.MCR.R & CFLASH_MCR_PEG) != CFLASH_MCR_PEG)
        {
            ret = 0x000000FFU;
        }

        /* Reset EHV */
        CFLASH.MCR.R = CFLASH_MCR_PGM;

        /* Reset PGM */
        CFLASH.MCR.R = CFLASH_MCR_NULL;

        /* Loop to wait for PGM = 0 */
        while ((CFLASH.MCR.R & CFLASH_MCR_PGM) == CFLASH_MCR_PGM)
        {
            ;
        }
    }

    return ret;
}

/*******************************************************************************
 * @brief      DFlash_BlockSelect_Cancel:
 *             Cancel block/sector selection.
 *             Page(8K) Erase:
 *             If you want to erase the page, 
 *             you need to cancel the sector selection.
 * @param[in]  None
 * @param[out] None
 * @retval     None
 * @notapi
 *******************************************************************************/
void DFlash_BlockSelect_Cancel(void)
{
    /** DFlash Low/Mid Address Space Block Select Register(DFLASH_LMS) **/
    DFLASH.LMS.R = 0x00000000U;
}

/*******************************************************************************
 * @brief      DFlash_PageErase
 * @param[in]  uint32_t addr
 * @param[out] None
 * @retval     uint32_t
 * @notapi
 *******************************************************************************/
uint32_t DFlash_PageErase(uint32_t addr)
{
    uint32_t ret = 0x00000000U;

    if ((DFLASH.MCR.R & (DFLASH_MCR_ERS | DFLASH_MCR_PGM)) != 0U)
    {
        /* If any P/E operation in progress return error */
        ret = 0x00000001U;
    }
    else
    {
        /* Set MCR ERS(Erase) bit */
        DFLASH.MCR.R = DFLASH_MCR_ERS;

        /* Interlock write */
        *((volatile uint32_t *)(addr)) = 0xFFFFFFFFU;

        /* Set MCR EHV(Enable High Voltage) bit, start operation */
        DFLASH.MCR.R |= DFLASH_MCR_EHV;

        /* Loop to wait for DONE = 1 */
        while ((DFLASH.MCR.R & DFLASH_MCR_DONE) != DFLASH_MCR_DONE)
        {
            ;
        }

        /* Check PEG(Program/Erase Good) flag */
        if ((DFLASH.MCR.R & DFLASH_MCR_PEG) != DFLASH_MCR_PEG)
        {
            ret = 0x000000FFU;
        }

        /* Reset EHV */
        DFLASH.MCR.R = DFLASH_MCR_ERS;

        /* Reset ERS */
        DFLASH.MCR.R = DFLASH_MCR_NULL;

        /* Loop to wait for ERS = 0 */
        while ((DFLASH.MCR.R & DFLASH_MCR_ERS) == DFLASH_MCR_ERS)
        {
            ;
        }
    }

    return ret;
}

/*******************************************************************************
 * @brief      DFlash Double Word Program.
 *              If you want to program the block, you must [unlock]
 *                  the block.
 * @param[in]  word1: word data 1
 *             word2: word data 2
 *             addr: destination address
 * @param[out] None
 * @retval     0: success, other: fail
 * @notapi
 *******************************************************************************/
uint32_t DFlash_DwProgram(uint32_t word1, uint32_t word2, uint32_t addr)
{
    uint32_t ret = 0x00000000U;
    uint32_t counter;                       /* Loop variable counter */

    /* Check operation */
    if ((DFLASH.MCR.R & (DFLASH_MCR_ERS | DFLASH_MCR_PGM)) != 0U)
    {
        /* Reuse variable counter for checking erase suspend */
        counter = DFLASH.MCR.R & (DFLASH_MCR_ERS | DFLASH_MCR_ESUS);
        if (counter == (DFLASH_MCR_ERS | DFLASH_MCR_ESUS))
        {
            /* Erase has been suspended
             *      and program operation can be performed. */
        }
        else
        {
            /* If any P/E operation in progress return error */
            ret = 0x00000001U;
        }
    }

    /* Program sequence */
    if (ret == 0x00000000U)
    {
        /* Set MCR PGM(Program) bit */
        DFLASH.MCR.R = DFLASH_MCR_PGM;

        /* Write interlock */
        *( (vuint32_t *)(addr) ) = word1;
        *( (vuint32_t *)(addr + 4U) ) = word2;

        /* Set MCR EHV(Enable High Voltage) bit, start operation */
        DFLASH.MCR.R |= DFLASH_MCR_EHV;

        /* Loop to wait for DONE = 1 */
        while ((DFLASH.MCR.R & DFLASH_MCR_DONE) != DFLASH_MCR_DONE)
        {
            ;
        }

        /* Check PEG(Program/Erase Good) flag */
        if ((DFLASH.MCR.R & DFLASH_MCR_PEG) != DFLASH_MCR_PEG)
        {
            ret = 0x000000FFU;
        }

        /* Reset EHV */
        DFLASH.MCR.R = DFLASH_MCR_PGM;

        /* Reset PGM */
        DFLASH.MCR.R = DFLASH_MCR_NULL;

        /* Loop to wait for PGM = 0 */
        while ((DFLASH.MCR.R & DFLASH_MCR_PGM) == DFLASH_MCR_PGM)
        {
            ;
        }
    }

    return ret;
}


